1 /** 2 * Utility code for path finding 3 * 4 * License: 5 * D version of code is under MIT. The original is under Apache 2.0. 6 * 7 * The MIT License (MIT) 8 * 9 * Copyright (c) 2014 Devisualization (Richard Andrew Cattermole) 10 * 11 * Permission is hereby granted, free of charge, to any person obtaining a copy 12 * of this software and associated documentation files (the "Software"), to deal 13 * in the Software without restriction, including without limitation the rights 14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 * copies of the Software, and to permit persons to whom the Software is 16 * furnished to do so, subject to the following conditions: 17 * 18 * The above copyright notice and this permission notice shall be included in all 19 * copies or substantial portions of the Software. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 * SOFTWARE. 28 * 29 * See_Also: 30 * http://www.redblobgames.com/pathfinding/a-star/implementation.html 31 */ 32 module devisualization.util.algorithms.pathfinding.defs; 33 deprecated("Killing"): 34 35 version(unittest) { 36 struct XY { 37 int x; 38 int y; 39 } 40 41 T from_id_width(T)(T v, T width) pure { return XY(id % width, id / width); } 42 enum XY[] DIAGRAM1_WALLS = [21,22,51,52,81,82,93,94,111,112,123,124,133,134,141,142,153,154,163,164,171,172,173,174,175,183,184,193,194,201,202,203,204,205,213,214,223,224,243,244,253,254,273,274,283,284,303,304,313,314,333,334,343,344,373,374,403,404,433,434].from_id_width; 43 44 GridWithWeights!(XY, int) diagram4() { 45 GridWithWeights!(XY, int) ret = GridWithWeights(10, 10); 46 ret.walls = [XY(1, 7), XY(1, 8), XY(2, 7), XY(2, 8), XY(3, 7), XY(3, 8)]; 47 ret.weights = [(3, 4), (3, 5), (4, 1), (4, 2), 48 (4, 3), (4, 4), (4, 5), (4, 6), 49 (4, 7), (4, 8), (5, 1), (5, 2), 50 (5, 3), (5, 4), (5, 5), (5, 6), 51 (5, 7), (5, 8), (6, 2), (6, 3), 52 (6, 4), (6, 5), (6, 6), (6, 7), 53 (7, 3), (7, 4), (7, 5)]; 54 return ret; 55 } 56 } 57 58 /** 59 * Does the type have: 60 * - x 61 * - y 62 * - x and y being both the same type 63 * - Ability to be initiated like a struct (think opCall for classes) 64 * 65 * Params: 66 * T = The type 67 * 68 * Returns: 69 * If the value is compliant 70 */ 71 bool hasXYPoints(T)() { 72 return __traits(hasMember, T, "x") && __traits(hasMember, T, "y") && 73 is(typeof(T.x) == typeof(T.y)) && 74 __traits(compiles, { T v = T(typeof(T.x).init, typeof(T.y).init); }); 75 } 76 77 /** 78 * The distance between two points 79 * Type of parameters must abide by hasXYPoints 80 * 81 * Params: 82 * a = The first position 83 * b = The second position 84 * 85 * Returns: 86 * The distance 87 * 88 * See_Also: 89 * hasXYPoints 90 */ 91 T heuristic(T, U = typeof(T.x))(T a, T b) if (hasXYPoints!T) { 92 import std.math : abs; 93 return cast(U)(abs(a.x - b.x) + abs(a.y - b.y)); 94 } 95 96 /** 97 * Turns the output from a search algorithm into a set path 98 * 99 * Params: 100 * came_from = The positions between start and end 101 * start = Starting position to go to 102 * goal = The end position 103 * 104 * Returns: 105 * A path from a position to another 106 * 107 * See_Also: 108 * dijkstra_search, a_star_search, breadth_first_search 109 */ 110 T[] reconstruct_path(T)(T[] came_from, T start, T goal) { 111 T current = goal; 112 T[] path = [current]; 113 114 while (current != start) { 115 current = came_from[current]; 116 path ~= current; 117 } 118 119 return path; 120 } 121 122 /** 123 * A simple graph structure 124 */ 125 struct Graph(T=string) { 126 private { 127 T[][T] edges_; 128 } 129 130 @property { 131 /** 132 * Get the edges this graph has 133 * 134 * Returns: 135 * The edges of the graph 136 */ 137 T[][T] edges() { return edges_; } 138 139 /** 140 * Set the edges this graph has 141 * 142 * Params: 143 * value = The new edges of the graph 144 */ 145 void edges(T[][T] value) { edges_ = value; } 146 } 147 148 /** 149 * Get a set edge, neighbors 150 * 151 * Params: 152 * The id of the edge 153 * 154 * Returns: 155 * The neighbor edges or null if id doesn't exist 156 */ 157 T[] neighbors(T id) { return edges_.get(id, null); } 158 } 159 160 /** 161 * A 2d grid that is rectangle in nature 162 * 163 * Params: 164 * T = A point type with x and y fields 165 * U = The type of x and y 166 */ 167 struct SquareGrid(T, U = typeof(T.x)) if (hasXYPoints!T) { 168 Graph this_; 169 alias this_ this; 170 171 private { 172 U width_; 173 U height_; 174 175 T[] walls_; 176 } 177 178 /** 179 * Creates the grid given the size 180 * 181 * Params: 182 * width = Width of the grid 183 * height = Height of the grid 184 */ 185 this(U width, U height) { 186 width_ = width; 187 height_ = height; 188 } 189 190 /** 191 * Is a given position within the grid 192 * 193 * Params: 194 * id = The position 195 * 196 * Returns: 197 * If the position is within the grid 198 */ 199 bool in_bounds(T id) { 200 return 0 <= id.x && id.x < width_ && 201 0 <= id.y && id.y < height_; 202 } 203 204 /** 205 * Is a given position blocked by a wall 206 * 207 * Params: 208 * id = The position 209 * 210 * Returns: 211 * If the position is blocked by a wall 212 */ 213 bool passable(T id) { 214 import std.algorithm : canFind; 215 return walls_.canFind(id); 216 } 217 218 /** 219 * Get a set edge, neighbors 220 * 221 * Params: 222 * id = The id of the edge 223 * 224 * Returns: 225 * The neighbor edges or null if id doesn't exist 226 */ 227 string[] neighbors(T id) { 228 import std.algorithm : reverse, filter, moveAll; 229 230 T[] results = [T(id.x + 1, id.y), T(id.x, id.y - 1), T(id.x - 1, id.y), T(id.x, id.y + y)]; 231 if ((id.x + id.y) % 2 == 0) 232 results = results.reverse; 233 234 filter!`in_bounds(a) && passable(a)`(results).moveAll(results); 235 236 return results; 237 } 238 } 239 240 /** 241 * A grid that has weighting per node 242 * 243 * Params: 244 * T = A point type with x and y fields 245 * U = The type of x and y 246 */ 247 struct GridWithWeights(T, U = ubyte, V = typeof(T.x)) { 248 SquareGrid!(T, V) this_; 249 alias this_ this; 250 251 private { 252 U[T] weights_; 253 } 254 255 /** 256 * Gets the weighting for an item 257 * 258 * Params: 259 * a = Previous position 260 * b = Current position 261 * default_ = Default value to return. Default: 1 262 */ 263 U cost(T a, T b, U default_ = 1) { 264 return weights_.get(b, default_); 265 } 266 } 267 268 /** 269 * A simple wrap around a DList 270 * 271 * See_Also: 272 * std.algorithm.DList 273 */ 274 struct Queue(T) { 275 DList!T this_; 276 alias this_ this; 277 278 /** 279 * Adds an item to the queue 280 * 281 * Params: 282 * x = The value to add 283 */ 284 void put(T x) { this_ ~= x; } 285 286 /** 287 * Gets the front item (FIFO) of the queue 288 * 289 * Returns: 290 * The first item in queue 291 */ 292 T get() { 293 T ret = this_.opSlice().front; 294 this_.opSlice().popFront; 295 return ret; 296 } 297 } 298 299 /** 300 * A wrapper around BinaryHeap 301 * Sorts a set of items based upon a priority 302 * 303 * Params: 304 * T = The queue item type 305 * U = The priority type 306 * 307 * See_Also: 308 * std.collections.binaryheap.BinaryHeap 309 */ 310 struct PriorityQueue(T, U=ubyte) { 311 struct PriorityQueueItem { 312 T item; 313 U priority; 314 } 315 316 BinaryHeap!(PriorityQueueItem, `a.priority < b.priority`) elements; 317 alias elements this; 318 319 /** 320 * Adds an item to the queue 321 * 322 * Params: 323 * item = The value to add 324 * priority = The priority of the item 325 */ 326 void put(T item, U priority) { elements ~= PriorityQueueItem(item, priority); } 327 328 /** 329 * Gets the front item (FIFO) of the queue 330 * 331 * Returns: 332 * The first item in queue sorted based upon the priority 333 */ 334 U get() { 335 T ret = this_.opSlice().front; 336 this_.opSlice().popFront; 337 return ret; 338 } 339 }